package com.gxa.hualianeshop.shiro;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.Filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * @author bill
 * @date 2023/7/6 11:48
 */
@Slf4j
public class JwtFilter extends BasicHttpAuthenticationFilter implements Filter {

    /**
     * 执行认证操作
     * @param request 请求对象
     * @param response 响应对象
     * @return 认证结果
     * @throws Exception 认证失败抛出根异常
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {

        // 从请求头中获取jwt
        String jwt = ((HttpServletRequest) request).getHeader("token");
        JwtToken jwtToken = new JwtToken(jwt);
        // 从过滤器中先获取认证主体
        // 认证主体调用login方法触发Realm的认证流程: doGetAuthenticationInfo
        getSubject(request, response).login(jwtToken);
        return true;
    }

    /**
     * 过滤器中的拦截操作
     * @param request http请求类型
     * @param response http响应类型
     * @param mappedValue 
     * @return
     */
    @SneakyThrows
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        log.info("==========被shiro拦截============");

        try {
            // 执行登录校验方法
            boolean accessSuccess = executeLogin(request, response);
            return accessSuccess;
        } catch (Exception e) {
            // 记录异常的原因
           log.error(e.getMessage());

           // 由于过滤器的执行在前, 无法触发springmvc
            // 也就没有全局异常处理, 故直接通过response给前端反馈
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json;charset=UTF-8");

            PrintWriter writer = response.getWriter();
            Map<String,Object> result = new HashMap<>();
            result.put("code","0");
            result.put("msg","用户认证不通过,请重新登录");

            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(result);
            writer.write(json);
            writer.flush();
            writer.close();

            return false;
        }
    }

    /**
     * 跨域的支持
     *
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        // 跨域时会首先发送一个option请求，这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }

}
